package l1j.server.encryptions;

import java.util.logging.Level;
import java.util.logging.Logger;

import l1j.server.server.types.UChar8;
import l1j.server.server.types.ULong32;


/**
 * 加密解密模組
 * @author daien
 *
 */
public class Encryption {

	private static final Logger _log = Logger.getLogger(Encryption.class.getName());

	private Keys _keys = null;

	/**
	 * 解密處理
	 * @param buf
	 * @param size
	 * @param currentKeys
	 * @return
	 */
	private byte[] _decrypt(final byte[] buf) {
		try {
			final char[] dk = UChar8.fromArray(this._keys.DKEY);

			final byte b3 = buf[3];
			buf[3] ^= dk[2];

			final byte b2 = buf[2];
			buf[2] ^= b3 ^ dk[3];

			final byte b1 = buf[1];
			buf[1] ^= b2 ^ dk[4];

			byte k = (byte) (buf[0] ^ b1 ^ dk[5]);
			buf[0] = (byte) (k ^ dk[0]);

			for (int i = 1; i < buf.length; i++) {
				final byte t = buf[i];
				buf[i] ^= dk[i & 7] ^ k;
				k = t;
			}

			return buf;

		} catch (final Exception e) {
			_log.log(Level.SEVERE ,e.getLocalizedMessage(), e);
		}
		return null;
	}

	/**
	 * 加密處理
	 * @param buf
	 * @param currentKeys
	 * @return
	 */
	private char[] _encrypt(final char[] buf) {
		try {
			final int size = buf.length;
			final char[] ek = UChar8.fromArray(this._keys.EKEY);

			buf[0] ^= ek[0];

			for (int i = 1; i < size; i++) {
				buf[i] ^= buf[i - 1] ^ ek[i & 7];
			}

			buf[3] = (char) (buf[3] ^ ek[2]);
			buf[2] = (char) (buf[2] ^ buf[3] ^ ek[3]);
			buf[1] = (char) (buf[1] ^ buf[2] ^ ek[4]);
			buf[0] = (char) (buf[0] ^ buf[1] ^ ek[5]);

			return buf;

		} catch (final Exception e) {
			_log.log(Level.SEVERE ,e.getLocalizedMessage(), e);
		}
		return null;
	}

	/**
	 * 解密
	 * @param buf byte[]
	 * @param length
	 * @param currentKeys
	 * @return
	 * @throws NoEncryptionKeysSelectedException
	 */
	public byte[] decrypt(final byte[] buf) throws Exception {
		try {
			if (this._keys == null) {
				throw new Exception();
			}
			//System.out.println("解密");
			this._decrypt(buf);

			final long mask = ULong32.fromArray(buf);

			this._keys.DKEY[0] ^= mask;
			this._keys.DKEY[1] = ULong32.add(this._keys.DKEY[1], 0x287EFFC3L);

			return buf;

		} catch (final Exception e) {
			_log.log(Level.SEVERE ,e.getLocalizedMessage(), e);
		}
		return null;
	}

	/**
	 * 加密
	 * @param buf char[]
	 * @param currentKeys LineageKeys
	 * @return
	 * @throws NoEncryptionKeysSelectedException
	 */
	public char[] encrypt(final char[] buf) throws Exception {
		try {
			if (this._keys == null) {
				throw new Exception();
			}
			//System.out.println("加密");
			final long mask = ULong32.fromArray(buf);

			this._encrypt(buf);

			this._keys.EKEY[0] ^= mask;
			this._keys.EKEY[1] = ULong32.add(this._keys.EKEY[1], 0x287EFFC3L);

			return buf;

		} catch (final Exception e) {
			_log.log(Level.SEVERE ,e.getLocalizedMessage(), e);
		}
		return null;
	}

	/**
	 * 設置加密解密模組
	 * @param clientID
	 * @param seed
	 * @throws ClientIdExistsException
	 */
	public void initKeys(final long seed) throws Exception {
		try {
			final Keys keys = new Keys();

			final long key[] = { seed, 0x930FD7E2L };

			Blowfish.getSeeds(key);

			keys.EKEY[0] = keys.DKEY[0] = key[0];
			keys.EKEY[1] = keys.DKEY[1] = key[1];

			this._keys = keys;
			//System.out.println(_keys.DKEY[0]+" / "+_keys.DKEY[1]);
			//System.out.println(_keys.EKEY[0]+" / "+_keys.EKEY[1]);
		} catch (final Exception e) {
			_log.log(Level.SEVERE ,e.getLocalizedMessage(), e);
		}
	}
}